import urllib
import cgi
from paste import request
variabledecode = None
__all__ = ['URL', 'Image']

def html_quote(v):
    if (v is None):
        return ''
    else:
        return cgi.escape(str(v), 1)



def url_quote(v):
    if (v is None):
        return ''
    else:
        return urllib.quote(str(v))


url_unquote = urllib.unquote

def js_repr(v):
    if (v is None):
        return 'null'
    if (v is False):
        return 'false'
    if (v is True):
        return 'true'
    if isinstance(v, list):
        return ('[%s]' % ', '.join(map(js_repr, v)))
    if isinstance(v, dict):
        return ('{%s}' % ', '.join([ ('%s: %s' % (js_repr(key), js_repr(value))) for (key, value,) in v ]))
    if isinstance(v, str):
        return repr(v)
    if isinstance(v, unicode):
        return repr(v.encode('UTF-8'))
    if isinstance(v, (float, int)):
        return repr(v)
    if isinstance(v, long):
        return repr(v).lstrip('L')
    if hasattr(v, '__js_repr__'):
        return v.__js_repr__()
    raise ValueError(("I don't know how to turn %r into a Javascript representation" % v))



class URLResource(object):
    default_params = {}

    def __init__(self, url, vars = None, attrs = None, params = None):
        self.url = (url or '/')
        self.vars = (vars or [])
        self.attrs = (attrs or {})
        self.params = self.default_params.copy()
        self.original_params = (params or {})
        if params:
            self.params.update(params)



    def from_environ(cls, environ, with_query_string = True, with_path_info = True, script_name = None, path_info = None, querystring = None):
        url = request.construct_url(environ, with_query_string=False, with_path_info=with_path_info, script_name=script_name, path_info=path_info)
        if with_query_string:
            if (querystring is None):
                vars = request.parse_querystring(environ)
            else:
                vars = cgi.parse_qsl(querystring, keep_blank_values=True, strict_parsing=False)
        else:
            vars = None
        v = cls(url, vars=vars)
        return v


    from_environ = classmethod(from_environ)

    def __call__(self, *args, **kw):
        res = self._add_positional(args)
        res = res._add_vars(kw)
        return res



    def __getitem__(self, item):
        if ('=' in item):
            (name, value,) = item.split('=', 1)
            return self._add_vars({url_unquote(name): url_unquote(value)})
        else:
            return self._add_positional((item))



    def attr(self, **kw):
        for key in kw.keys():
            if key.endswith('_'):
                kw[key[:-1]] = kw[key]
                del kw[key]

        new_attrs = self.attrs.copy()
        new_attrs.update(kw)
        return self.__class__(self.url, vars=self.vars, attrs=new_attrs, params=self.original_params)



    def param(self, **kw):
        new_params = self.original_params.copy()
        new_params.update(kw)
        return self.__class__(self.url, vars=self.vars, attrs=self.attrs, params=new_params)



    def coerce_vars(self, vars):
        global variabledecode
        need_variable_encode = False
        for (key, value,) in vars.items():
            if isinstance(value, dict):
                need_variable_encode = True
            if key.endswith('_'):
                vars[key[:-1]] = vars[key]
                del vars[key]

        if need_variable_encode:
            if (variabledecode is None):
                from formencode import variabledecode
            vars = variabledecode.variable_encode(vars)
        return vars



    def var(self, **kw):
        kw = self.coerce_vars(kw)
        new_vars = (self.vars + kw.items())
        return self.__class__(self.url, vars=new_vars, attrs=self.attrs, params=self.original_params)



    def setvar(self, **kw):
        kw = self.coerce_vars(kw)
        new_vars = []
        for (name, values,) in self.vars:
            if (name in kw):
                continue
            new_vars.append((name, values))

        new_vars.extend(kw.items())
        return self.__class__(self.url, vars=new_vars, attrs=self.attrs, params=self.original_params)



    def setvars(self, **kw):
        return self.__class__(self.url, vars=kw.items(), attrs=self.attrs, params=self.original_params)



    def addpath(self, *paths):
        u = self
        for path in paths:
            path = str(path).lstrip('/')
            new_url = u.url
            if not new_url.endswith('/'):
                new_url += '/'
            u = u.__class__((new_url + path), vars=u.vars, attrs=u.attrs, params=u.original_params)

        return u


    __div__ = addpath

    def become(self, OtherClass):
        return OtherClass(self.url, vars=self.vars, attrs=self.attrs, params=self.original_params)



    def href__get(self):
        s = self.url
        if self.vars:
            s += '?'
            vars = []
            for (name, val,) in self.vars:
                if isinstance(val, (list, tuple)):
                    val = [ v for v in val if (v is not None) ]
                elif (val is None):
                    continue
                vars.append((name, val))

            s += urllib.urlencode(vars, True)
        return s


    href = property(href__get)

    def __repr__(self):
        base = ('<%s %s' % (self.__class__.__name__, (self.href or "''")))
        if self.attrs:
            base += (' attrs(%s)' % ' '.join([ ('%s="%s"' % (html_quote(n), html_quote(v))) for (n, v,) in self.attrs.items() ]))
        if self.original_params:
            base += (' params(%s)' % ', '.join([ ('%s=%r' % (n, v)) for (n, v,) in self.attrs.items() ]))
        return (base + '>')



    def html__get(self):
        if not self.params.get('tag'):
            raise ValueError(("You cannot get the HTML of %r until you set the 'tag' param'" % self))
        content = self._get_content()
        tag = ('<%s' % self.params.get('tag'))
        attrs = ' '.join([ ('%s="%s"' % (html_quote(n), html_quote(v))) for (n, v,) in self._html_attrs() ])
        if attrs:
            tag += (' ' + attrs)
        tag += self._html_extra()
        if (content is None):
            return (tag + ' />')
        else:
            return ('%s>%s</%s>' % (tag,
             content,
             self.params.get('tag')))


    html = property(html__get)

    def _html_attrs(self):
        return self.attrs.items()



    def _html_extra(self):
        return ''



    def _get_content(self):
        raise NotImplementedError



    def _add_vars(self, vars):
        raise NotImplementedError



    def _add_positional(self, args):
        raise NotImplementedError




class URL(URLResource):
    default_params = {'tag': 'a'}

    def __str__(self):
        return self.href



    def _get_content(self):
        if not self.params.get('content'):
            raise ValueError(('You must give a content param to %r generate anchor tags' % self))
        return self.params['content']



    def _add_vars(self, vars):
        url = self
        for name in ('confirm', 'content'):
            if (name in vars):
                url = url.param(**{name: vars.pop(name)})

        if ('target' in vars):
            url = url.attr(target=vars.pop('target'))
        return url.var(**vars)



    def _add_positional(self, args):
        return self.addpath(*args)



    def _html_attrs(self):
        attrs = self.attrs.items()
        attrs.insert(0, ('href', self.href))
        if self.params.get('confirm'):
            attrs.append(('onclick', ('return confirm(%s)' % js_repr(self.params['confirm']))))
        return attrs



    def onclick_goto__get(self):
        return ('location.href=%s; return false' % js_repr(self.href))


    onclick_goto = property(onclick_goto__get)

    def button__get(self):
        return self.become(Button)


    button = property(button__get)

    def js_popup__get(self):
        return self.become(JSPopup)


    js_popup = property(js_popup__get)


class Image(URLResource):
    default_params = {'tag': 'img'}

    def __str__(self):
        return self.html



    def _get_content(self):
        return 



    def _add_vars(self, vars):
        return self.attr(**vars)



    def _add_positional(self, args):
        return self.addpath(*args)



    def _html_attrs(self):
        attrs = self.attrs.items()
        attrs.insert(0, ('src', self.href))
        return attrs




class Button(URLResource):
    default_params = {'tag': 'button'}

    def __str__(self):
        return self.html



    def _get_content(self):
        if self.params.get('content'):
            return self.params['content']
        if self.attrs.get('value'):
            return self.attrs['content']



    def _add_vars(self, vars):
        button = self
        if ('confirm' in vars):
            button = button.param(confirm=vars.pop('confirm'))
        if ('content' in vars):
            button = button.param(content=vars.pop('content'))
        return button.var(**vars)



    def _add_positional(self, args):
        return self.addpath(*args)



    def _html_attrs(self):
        attrs = self.attrs.items()
        onclick = ('location.href=%s' % js_repr(self.href))
        if self.params.get('confirm'):
            onclick = ('if (confirm(%s)) {%s}' % (js_repr(self.params['confirm']), onclick))
        onclick += '; return false'
        attrs.insert(0, ('onclick', onclick))
        return attrs




class JSPopup(URLResource):
    default_params = {'tag': 'a',
     'target': '_blank'}

    def _add_vars(self, vars):
        button = self
        for var in ('width', 'height', 'stripped', 'content'):
            if (var in vars):
                button = button.param(**{var: vars.pop(var)})

        return button.var(**vars)



    def _window_args(self):
        p = self.params
        features = []
        if p.get('stripped'):
            p['location'] = p['status'] = p['toolbar'] = '0'
        for param in 'channelmode directories fullscreen location menubar resizable scrollbars status titlebar'.split():
            if (param not in p):
                continue
            v = p[param]
            if (v not in ('yes', 'no', '1', '0')):
                if v:
                    v = '1'
                else:
                    v = '0'
            features.append(('%s=%s' % (param, v)))

        for param in 'height left top width':
            if not p.get(param):
                continue
            features.append(('%s=%s' % (param, p[param])))

        args = [self.href, p['target']]
        if features:
            args.append(','.join(features))
        return ', '.join(map(js_repr, args))



    def _html_attrs(self):
        attrs = self.attrs.items()
        onclick = ('window.open(%s); return false' % self._window_args())
        attrs.insert(0, ('target', self.params['target']))
        attrs.insert(0, ('onclick', onclick))
        attrs.insert(0, ('href', self.href))
        return attrs



    def _get_content(self):
        if not self.params.get('content'):
            raise ValueError(('You must give a content param to %r generate anchor tags' % self))
        return self.params['content']



    def _add_positional(self, args):
        return self.addpath(*args)



if (__name__ == '__main__'):
    import doctest
    doctest.testmod()

